package summer.codegenerator.model.base;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 获取持久化对象的一个全新副本
 * 该副本有以下两个特性：
 *  1 消除对象之间相互引用，可序列化；
 *  	Car {
 *  	    String color;
 *  	    People owner;
 *  	}
 *  	People {
 *  		String name;
 *  		String phoneNo;
 *  		List<Car> cars;
 *  	}
 *  	当获取Car副本时，其owner字段的cars字段中Car对象将为空
 *  	当获取People副本时，其cars字段中Car对象的owner对象将为空
 *  2 处于新建状态，对副本的修改不会反应到数据库
 *
 *  支持字段：
 *  byte,Byte
 *  short,Short
 *  int,Integer
 *  long,Long
 *  boolean,Boolean
 *  char,Character
 *  float,Float
 *  double,Double
 *
 *  java.math.BigDecimal
 *  java.util.List
 *  java.util.Map
 *  java.util.Date
 *  java.sql.Date
 *
 * @author 蒋振
 * @since 2019年7月4日 下午6:39:10
 */
public class Duplicate {

	private static int OCCUR_MAX = 2;

	public static Object dp(Object source, Class<?>... superiorClazzes) {
		Duplicate dp = new Duplicate(source, superiorClazzes);
		return dp.duplicate();
	}

	// 待复制对象
	private Object source;
	// 待复制类对象
	private Class<?> sourceClazz;
	// 已参与类对象列表
	private Class<?>[] superiorClazzes;

	private Duplicate(Object source, Class<?>... superiorClazzes) {
		this.source = source;
		this.sourceClazz = source.getClass();
		this.superiorClazzes = superiorClazzes;
		// 将当前类对象加入到类对象列表
		this.superiorClazzes = this.duplicateSuperiorClazzes(true);
	}

	private Object duplicate() {
		Object target = null;
		try {
			target = this.sourceClazz.newInstance();
			Field[] fields = this.sourceClazz.getDeclaredFields();
			List<String> fieldsCopied = new LinkedList<String>();
			for (Field field : fields) {
				String fieldName = field.getName();
				Class<?> fieldType = field.getType();
				if (this.basicFieldType(fieldType)) {
					continue;
				}
				// getter
				Method getMethod = this.sourceClazz.getDeclaredMethod("get" + StringUtils.capitalize(fieldName));
				// setter
				Method setMethod = this.sourceClazz.getDeclaredMethod("set" + StringUtils.capitalize(fieldName),
						fieldType);
				if(getMethod == null || setMethod == null) {
					continue;
				}
				// copy field for 非基础类型字段
				this.copyField(source, target, fieldType, getMethod, setMethod);

				fieldsCopied.add(fieldName);
			}

			BeanUtils.copyProperties(source, target,
					fieldsCopied.toArray(new String[fieldsCopied.size()]));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return target;
	}

	private void copyField(Object source, Object target, Class<?> fieldType, Method getMethod, Method setMethod) {
		try {
			Object value = getMethod.invoke(source);
			if (value == null) {
				return;
			}
			if (fieldType.isAssignableFrom(List.class)) {
				List sourceList = (List) value;
				if (!CollectionUtils.isEmpty(sourceList)) {
					List targetList = this.copyList(sourceList, superiorClazzes);
					setMethod.invoke(target, targetList);
				}
			} else if (fieldType.isAssignableFrom(Set.class)) {
				Set sourceSet = (Set) value;
				if (!CollectionUtils.isEmpty(sourceSet)) {
					Set targetSet = this.copySet(sourceSet, superiorClazzes);
					setMethod.invoke(target, targetSet);
				}
			} else if (fieldType.isAssignableFrom(Map.class)) {
				Map sourceMap = (Map) value;
				if (!CollectionUtils.isEmpty(sourceMap)) {
					Map targetMap = this.copyMap(sourceMap, superiorClazzes);
					setMethod.invoke(target, targetMap);
				}
			} else {
				int count = this.classCount(fieldType, superiorClazzes);
				if (count < OCCUR_MAX) {
					setMethod.invoke(target, Duplicate.dp(value, this.duplicateSuperiorClazzes(false)));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 复制Map对象
	 *
	 *
	 * @param sourceMap
	 * @param superiorClazzes
	 * @return
	 * @author: jiangz
	 * @date: Created on 2019年9月26日 下午2:15:56
	 */
	private Map copyMap(Map sourceMap, Class<?>... superiorClazzes) {
		Map targetMap = new HashMap();
		if (!CollectionUtils.isEmpty(sourceMap)) {
			// 目标列表
			for (Object key : sourceMap.keySet()) {
				Object item = sourceMap.get(key);
				Class<?> itemClass = item.getClass();
				if (this.basicFieldType(itemClass)) {
					targetMap.put(key, item);
				} else {
					int count = this.classCount(itemClass, superiorClazzes);
					if (count < OCCUR_MAX) {
						targetMap.put(key,Duplicate.dp(item, this.duplicateSuperiorClazzes(false)));
					}
				}
			}
		}
		return targetMap;
	}

	/**
	 * 复制List对象
	 *
	 *
	 * @param sourceList
	 * @param superiorClazzes
	 * @return
	 * @author: jiangz
	 * @date: Created on 2019年9月26日 下午2:15:56
	 */
	private List copyList(List sourceList, Class<?>... superiorClazzes) {
		List targetList = new LinkedList<>();
		if (!CollectionUtils.isEmpty(sourceList)) {
			// 目标列表
			for (Object item : sourceList) {
				Class<?> itemClass = item.getClass();
				if (this.basicFieldType(itemClass)) {
					targetList.add(item);
				} else {
					int count = this.classCount(itemClass, superiorClazzes);
					if (count < OCCUR_MAX) {
						targetList.add(Duplicate.dp(item, this.duplicateSuperiorClazzes(false)));
					}
				}
			}
		}
		return targetList;
	}

	/**
	 * 复制Set对象
	 * 只有一层
	 *
	 * @param sourceSet
	 * @param superiorClazzes
	 * @return
	 * @author: jiangz
	 * @date: Created on 2019年9月26日 下午2:15:56
	 */
	private Set copySet(Set sourceSet, Class<?>... superiorClazzes) {
		Set targetSet = new HashSet();
		if (!CollectionUtils.isEmpty(sourceSet)) {
			// 目标列表
			for (Object item : sourceSet) {
				Class<?> itemClass = item.getClass();
				if (this.basicFieldType(itemClass)) {
					targetSet.add(item);
				} else {
					int count = this.classCount(itemClass, superiorClazzes);
					if (count < OCCUR_MAX) {
						targetSet.add(Duplicate.dp(item, this.duplicateSuperiorClazzes(false)));
					}
				}
			}
		}
		return targetSet;
	}

	/**
	 * 复制一份模型对象层级接口
	 *
	 *
	 * @param withSourceClazzes
	 * @return
	 * @author: jiangz
	 * @date: Created on 2019年9月26日 下午2:17:43
	 */
	private Class<?>[] duplicateSuperiorClazzes(boolean withSourceClazzes){
		List<Class<?>> clazzList = new LinkedList<Class<?>>();
		if(this.superiorClazzes != null && this.superiorClazzes.length > 0) {
			for(Class<?> clazz : this.superiorClazzes) {
				clazzList.add(clazz);
			}
		}
		if(withSourceClazzes) {
			clazzList.add(sourceClazz);
		}
		return clazzList.toArray(new Class<?>[clazzList.size()]);
	}

	/**
	 * 查询一个模型对象在树状结构中出现的次数
	 *
	 *
	 * @param targetClazz
	 * @param clazzes
	 * @return
	 * @author: jiangz
	 * @date: Created on 2019年9月26日 下午2:13:20
	 */
	private Integer classCount(Class<?> targetClazz, Class<?>... clazzes) {
		int count = 0;
		if (clazzes != null && clazzes.length > 0) {
			for (Class<?> clazz : clazzes) {
				if (Objects.equals(clazz, targetClazz)) {
					count++;
				}
			}
			return count;
		}
		if (Objects.equals(targetClazz, this.sourceClazz)) {
			count++;
		}
		return count;
	}


	private boolean basicFieldType(Class<?> fieldType) {
		if (fieldType.isPrimitive()
				|| // 基本类型
				fieldType.isEnum()
				|| // 枚举
				fieldType.equals(Boolean.class)
				|| // 基本类型的包装类型
				fieldType.equals(Character.class) || fieldType.equals(Byte.class) || fieldType.equals(Short.class)
				|| fieldType.equals(Integer.class) || fieldType.equals(Long.class) || fieldType.equals(Float.class)
				|| fieldType.equals(Double.class) || fieldType.equals(String.class)
				|| // 数字
				fieldType.equals(BigDecimal.class)
				|| // 日期类型
				fieldType.equals(Calendar.class) || fieldType.equals(java.util.Date.class) || fieldType.equals(java.sql.Date.class)) {
			return true;
		} else {
			return false;
		}
	}
}
